﻿using LJ.Abstractions.Extensions;
using LJ.WebApi.Abstractions.DynamicWebApi.Attributes;
using LJ.WebApi.Mvc.Pack;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace LJ.WebApi.Mvc.DynamicWebApi.Internal;

public partial class DynamicApiServiceConvention(IServiceProvider serviceProvider, IDynamicWebApiManager dynamicWebApiManager) : IDynamicApiServiceConvention
{
    [GeneratedRegex(@"V(?<version>[0-9_]+$)")]
    public static partial Regex NameVersionRegex();

    private readonly WebApiConfig _webApiConfig = serviceProvider.GetConfig<WebApiConfig>();
    private readonly IDynamicWebApiManager _dynamicWebApiManager = dynamicWebApiManager;
    private readonly IEnumerable<IConfigureAction> _configureActions = serviceProvider.GetServices<IConfigureAction>();
    public void Apply(ApplicationModel application)
    {

        var controllers = application.Controllers.Where(u => _dynamicWebApiManager.IsApiController(u.ControllerType));

        foreach (var controller in controllers)
        {
            var dynamicWebApiAttribute = _dynamicWebApiManager.GetDynamicWebApiAttribute(controller.ControllerType);

            if (dynamicWebApiAttribute != null)
            {
                ConfigureDynamicWebApi(controller, dynamicWebApiAttribute);
            }

        }
    }

    #region ConfigureDynamicWebApi
    private void ConfigureDynamicWebApi(ControllerModel controller, DynamicWebApiAttribute dynamicWebApiAttribute)
    {
        ConfigureController(controller, dynamicWebApiAttribute);
        ConfigureApiExplorer(controller, dynamicWebApiAttribute);
        ConfigureSelector(controller, dynamicWebApiAttribute);
        ConfigureParameters(controller);

        ConfigureActions(controller);
    }

   

    #endregion

    #region ConfigureController
    private static void ConfigureController(ControllerModel controller, DynamicWebApiAttribute dynamicWebApiAttribute)
    {
        controller.ControllerName = dynamicWebApiAttribute.Module;
    }
    #endregion

    #region ConfigureSelector
    private void ConfigureSelector(ControllerModel controller, DynamicWebApiAttribute controllerAttr)
    {

        if (controller.Selectors.Any(selector => selector.AttributeRouteModel != null))
        {
            return;
        }

        foreach (var action in controller.Actions)
        {
            var dynamicWebApiMethodAttribute = _dynamicWebApiManager.GetDynamicWebApiMethodAttribute(action.ActionMethod);
            if (dynamicWebApiMethodAttribute!=null)
            {
                ConfigureSelector(controllerAttr.Module, action);
            }
        }
    }
    private void ConfigureSelector( string controllerName, ActionModel action)
    {
        var dynamicWebApiMethodAttribute = _dynamicWebApiManager.GetDynamicWebApiMethodAttribute(action.ActionMethod);
        if (dynamicWebApiMethodAttribute == null)
        {
            return ;
        }
        action.ActionName = dynamicWebApiMethodAttribute.Action;

        if (action.Selectors == null || action.Selectors.Any(a => a.ActionConstraints == null))
        {
            AddAppServiceSelector( controllerName, action, dynamicWebApiMethodAttribute.HttpMethod);
        }
        else
        {
            NormalizeSelectorRoutes( controllerName, action);
        }
    }
    private void AddAppServiceSelector( string controllerName, ActionModel action, HttpMethod  httpMethod )
    {

        var appServiceSelectorModel = action.Selectors[0];

        appServiceSelectorModel.AttributeRouteModel ??= CreateActionRouteModel( controllerName,  action);

        if (!appServiceSelectorModel.ActionConstraints.Any())
        {
          
            appServiceSelectorModel.ActionConstraints.Add(new HttpMethodActionConstraint([httpMethod.Method]));
            switch (httpMethod.Method)
            {
                case "GET":
                    appServiceSelectorModel.EndpointMetadata.Add(new HttpGetAttribute());
                    break;
                case "POST":
                    appServiceSelectorModel.EndpointMetadata.Add(new HttpPostAttribute());
                    break;
                case "PUT":
                    appServiceSelectorModel.EndpointMetadata.Add(new HttpPutAttribute());
                    break;
                case "DELETE":
                    appServiceSelectorModel.EndpointMetadata.Add(new HttpDeleteAttribute());
                    break;
                default:
                    throw new Exception($"Unsupported http verb: {httpMethod.Method}.");
            }
        }
    }


    private void NormalizeSelectorRoutes( string controllerName, ActionModel action)
    {
        foreach (var selector in action.Selectors)
        {
            selector.AttributeRouteModel = selector.AttributeRouteModel == null ?
                 CreateActionRouteModel( controllerName, action) :
                 AttributeRouteModel.CombineAttributeRouteModel(CreateActionRouteModel( controllerName, action), selector.AttributeRouteModel);
        }
    }
    #endregion


    #region ConfigureApiExplorer
    private void ConfigureApiExplorer(ControllerModel controller, DynamicWebApiAttribute dynamicWebApiAttribute)
    {
        if (string.IsNullOrEmpty(controller.ApiExplorer.GroupName))
        {
            controller.ApiExplorer.GroupName = dynamicWebApiAttribute.Module;
        }

        if (controller.ApiExplorer.IsVisible == null)
        {
            controller.ApiExplorer.IsVisible = true;
        }

        foreach (var action in controller.Actions)
        {
            var dynamicWebApiMethodAttribute = _dynamicWebApiManager.GetDynamicWebApiMethodAttribute(action.ActionMethod);
            if (dynamicWebApiMethodAttribute != null)
            {
                if (action.ApiExplorer.IsVisible == null)
                {
                    action.ApiExplorer.IsVisible = true;
                }
            }
            else
            {
                action.ApiExplorer.IsVisible = false;
            }
        }
    }

    #endregion


    #region ConfigureAction
    private void ConfigureActions(ControllerModel controller)
    {
        foreach (var action in controller.Actions)
        {
            foreach (var configureAction in _configureActions)
            {
                configureAction.Configure(action);
            }
        }
    }
    #endregion


    #region ConfigureParameters
    private static void ConfigureParameters(ControllerModel controller)
    {
        foreach (var action in controller.Actions)
        {
            foreach (var parameter in action.Parameters)
            {
                if (parameter.BindingInfo != null)
                    continue;

                if (!parameter.ParameterType.IsPrimitiveExtendedIncludingNullable())
                    if (CanUseFormBodyBinding(action))
                        parameter.BindingInfo = BindingInfo.GetBindingInfo([new FromBodyAttribute()]);
            }
        }
    }

    #endregion

    #region private
    /// <summary>
    /// 是否可以使用Body传参
    /// </summary>
    /// <param name="action"></param>
    /// <returns></returns>
    private static bool CanUseFormBodyBinding(ActionModel action)
    {
        var httpMethods = action.Selectors.SelectMany(selector => selector.ActionConstraints)
            .OfType<HttpMethodActionConstraint>().SelectMany(temp => temp.HttpMethods).ToList();

        if (httpMethods.Contains("GET") || httpMethods.Contains("TRACE") || httpMethods.Contains("HEAD"))
            return false;

        return true;
    }

    private AttributeRouteModel CreateActionRouteModel( string controllerName, ActionModel action)
    {
        var route = $"{_webApiConfig.ApiPrefix}/{controllerName}/{action.ActionName}".Replace("//", "/");

        return new AttributeRouteModel(new RouteAttribute(route));
    }

    #endregion
}
